機械学習でプログラミング言語を推定したい
はじめに
機械学習って楽しそうです。どうせならあんまりサンプル落ちてないようなことやりたかったので、表題の通りプログラミング言語を推定してみます。
今回は僕の独断と偏見で、
- Python
- C++
- Go
- Java
- Ruby
- TypeScript
これらの言語で遊んでみます。
やること
- 学習データを用意する
- 学習させる
- 推定する
学習データ集め
これが大変ですね。GitHubからガーーーーーーッと持ってこれたら楽なんですが、規約的にスーパーグレーゾーンな気がしたのでやめました。
代わりに、Copilotに頼んで幾つかコードを生成してもらい、それを学習データとして用います。全く
知見がないのでどれだけ集めればいいかわかりませんが、とりあえず各言語20ファイルずつ用意しました。
学習
ファイル丸ごとベクトル化してロジスティック回帰に投げます。行ごと、トークンごとにベクトル化するのも試しましたが、ファイル丸ごとが一番精度を伸ばせそうでした。
コード
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
# データセットの準備
# Xはコードスニペットのリスト、yは対応するプログラミング言語のリスト
X = []
y = []
def read_code_snipets(langage: str):
with open(f"learning-data/{langage}/metadata.txt", "r") as f:
# metadataフォーマット
# [num of file]: int, [label]: str
num_of_file, label = f.readline().split(", ")
for f_name in range(1, int(num_of_file) + 1):
with open(f"learning-data/{langage}/{f_name}.txt", "r") as f:
lines = f.readlines()
file = "".join(lines)
X.append(file)
y.append(label)
langages = ["python", "cpp", "golang", "ruby", "java", "typescript"]
for langage in langages:
read_code_snipets(langage)
# データの前処理と特徴量の抽出
vectorizer = TfidfVectorizer()
X_vectorized = vectorizer.fit_transform(X)
# データの分割
X_train, X_test, y_train, y_test = train_test_split(
X_vectorized, y, test_size=0.2, random_state=42
)
# モデルの訓練
classifier = LogisticRegression()
classifier.fit(X_train, y_train)
# モデルの評価
y_pred = classifier.predict(X_test)
print(classification_report(y_test, y_pred))
学習データをlearning-dataディレクトリに格納し、言語ごとにtxtファイルとして保存しています。metadata.txtにファイル数とラベルを記載しているので、それを読んで適切にデータを集めていきます。
結果
とりあえず20ファイルずつ用意してやってみました。2割がテストデータになるので、学習したのは16ファイルです。
Precision | Recall | F1-Score | Support | |
---|---|---|---|---|
cpp | 0.50 | 1.00 | 0.67 | 3 |
golang | 1.00 | 0.60 | 0.75 | 5 |
java | 1.00 | 0.33 | 0.50 | 3 |
python | 0.71 | 1.00 | 0.83 | 5 |
ruby | 1.00 | 0.80 | 0.89 | 5 |
typescript | 1.00 | 1.00 | 1.00 | 3 |
accuracy | 0.79 | 24 | ||
macro avg | 0.87 | 0.79 | 0.77 | 24 |
weighted avg | 0.88 | 0.79 | 0.79 | 24 |
現時点で正解率79%です。まあ、ぼちぼちなんですかね。
なんか微妙な気がしたので気合いで新たに20ファイルずつ作ってもらいました。Copilotの出力を必死にコピペして学習データ作ってるんですが、アナログ作業が結局一番辛いです。
結果は、
Language | Precision | Recall | F1-Score | Support |
---|---|---|---|---|
cpp | 1.00 | 0.75 | 0.86 | 8 |
golang | 0.83 | 0.83 | 0.83 | 12 |
java | 0.71 | 1.00 | 0.83 | 5 |
python | 1.00 | 1.00 | 1.00 | 7 |
ruby | 1.00 | 1.00 | 1.00 | 8 |
typescript | 1.00 | 1.00 | 1.00 | 8 |
accuracy | 0.92 | 48 | ||
macro avg | 0.92 | 0.93 | 0.92 | 48 |
weighted avg | 0.93 | 0.92 | 0.92 | 48 |
となりました。正解率92%まで上がりました。他のスコアも上がってて多分いい感じなんだと思います。
おまけ
おまけ1
優秀な副操縦士に渡したプロンプトはこんな感じです。
以下リストの各言語を使ったプログラムを、お題に合わせて作成して別々に出力してください。出力にはコメントを含めないでください。 省略したくても必ずリストの全てについて出力してください。リスト:[python, c++, go, ruby, typescript, java] お題: xxxxx
省略したくても必ずリストの全てについて出力してください。
これを入れないと、Pythonだけ出して『あとは省略します』とか言い始めることがありました。
おまけ2
この学習データを使って、学習させていない言語について推定させてみました。確率はpredict_probaの結果です。
推定してもらう言語 | 推定結果 | 確率 |
---|---|---|
Lua | Ruby | 45% |
Rust | TypeScript | 23% |
PowerShell | Python | 29% |
Fortran | Ruby | 34% |
ほーーーん、という感じです。
終わりに
こんだけちゃんと機械学習したのは初めてなので楽しかったです。